package com.example.user.service;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.commons.response.ResponseResult;
import com.example.commons.response.RestResultUtil;
import com.example.commons.util.DateUtils;
import com.example.commons.util.HttpServletUtils;
import com.example.user.config.WxPayConfig;
import com.example.user.entity.wx.CreateOrderReq;
import com.example.user.entity.wx.QueryOrderReq;
import com.example.user.entity.wx.WxOrderEntity;
import com.example.user.entity.wx.WxPayLogEntity;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import java.sql.Wrapper;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import static org.bouncycastle.asn1.x500.style.RFC4519Style.description;


/**
 * Author:汪彬彬
 * date:2024/11/12 14:54
 */

@Slf4j
//@Service
public class WxPayService {
    @Resource
    private WxPayConfig wxPayConfig;
    @Autowired
    private RSAAutoCertificateConfig rsaAutoCertificateConfig;
    @Autowired
    private WxOrderDataService wxOrderDataService;
    @Autowired
    private WxPayLogDataService wxPayLogDataService;

    /***
     * 预支付订单
     * @param req
     * @return
     */
    public ResponseResult createOrder(CreateOrderReq req) throws Exception {
        if (req == null) {
            return RestResultUtil.error("创建订单失败，缺少参数！");
        }
        //先解密

        String orderNo = req.getOutTradeNo();
        Integer totalFee = req.getTotal();
        //创建初始化订单
        //todo,创建订单这边你们自己来（后面我会放出表结构）
        //请求微信支付相关配置
        JsapiServiceExtension service =
                new JsapiServiceExtension.Builder()
                        .config(rsaAutoCertificateConfig)
                        .signType("RSA") // 不填默认为RSA
                        .build();
        PrepayWithRequestPaymentResponse response = new PrepayWithRequestPaymentResponse();
        try {
            PrepayRequest request = new PrepayRequest();
            request.setAppid(wxPayConfig.getAppId());
            request.setMchid(wxPayConfig.getMerchantId());
            request.setDescription(String.valueOf(description));
            request.setOutTradeNo(orderNo);
            request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
            Amount amount = new Amount();
            //amount.setTotal(totalFee.multiply(new BigDecimal("100")).intValue());
            amount.setTotal(totalFee);
            request.setAmount(amount);
            Payer payer = new Payer();
            payer.setOpenid(req.getWxOpenId());
            request.setPayer(payer);
            log.info("请求预支付下单，请求参数：{}", JSONObject.toJSONString(request));
            // 调用预下单接口
            response = service.prepayWithRequestPayment(request);
            log.info("订单【{}】发起预支付成功，返回信息：{}", orderNo, response);
        } catch (HttpException e) { // 发送HTTP请求失败
            log.error("微信下单发送HTTP请求失败，错误信息：{}", e.getHttpRequest());
            return RestResultUtil.error("下单失败");
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
            log.error("微信下单服务状态错误，错误信息：{}", e.getErrorMessage());
            return RestResultUtil.error("下单失败");
        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
            log.error("服务返回成功，返回体类型不合法，或者解析返回体失败，错误信息：{}", e.getMessage());
            return RestResultUtil.error("下单失败");
        }

        return RestResultUtil.success(response);
    }

    /***
     * 微信支付回调通知
     * @param request
     * @return
     * @throws
     */
    @Transactional
    public synchronized String payNotify(HttpServletRequest request) throws Exception {
        log.info("------收到支付通知------");
        // 请求头Wechatpay-Signature
        String signature = request.getHeader("Wechatpay-Signature");
        // 请求头Wechatpay-nonce
        String nonce = request.getHeader("Wechatpay-Nonce");
        // 请求头Wechatpay-Timestamp
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        // 微信支付证书序列号
        String serial = request.getHeader("Wechatpay-Serial");
        // 签名方式
        String signType = request.getHeader("Wechatpay-Signature-Type");
        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .signType(signType)
                .body(HttpServletUtils.getRequestBody(request))
                .build();

        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
        // 以支付通知回调为例，验签、解密并转换成 Transaction
        log.info("验签参数：{}", requestParam);
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        log.info("验签成功！-支付回调结果：{}", transaction.toString());

        Map<String, String> returnMap = new HashMap<>(2);
        returnMap.put("code", "FAIL");
        returnMap.put("message", "失败");
        //修改订单前，建议主动请求微信查询订单是否支付成功，防止恶意post
        QueryWrapper<WxOrderEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("out_trade_no", transaction.getOutTradeNo());
        //wrapper.eq("transaction_id", transaction.getTransactionId());
        WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
        if (wxOrderEntity != null) {
            if (wxOrderEntity.getPayStatus() == 1) {
                //此时已经是支付成功，不在处理订单信息
                returnMap.put("code", "SUCCESS");
                returnMap.put("message", "成功");
                return JSONObject.toJSONString(returnMap);
            }
        }
        if (Transaction.TradeStateEnum.SUCCESS != transaction.getTradeState()) {
            log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", transaction.getOutTradeNo(), transaction.getTransactionId());
            if (wxOrderEntity != null) {
                wxOrderEntity.setUpdateTime(new Date());
                wxOrderEntity.setPayStatus(2);
                wxOrderEntity.setPayNonce(nonce);
                wxOrderEntity.setTransactionId(transaction.getTransactionId());
                //修改订单信息
                wxOrderDataService.updateById(wxOrderEntity);
            }
            return JSONObject.toJSONString(returnMap);
        }
        if (wxOrderEntity != null) {
            wxOrderEntity.setUpdateTime(new Date());
            wxOrderEntity.setPayTime(DateUtils.string2Date(transaction.getSuccessTime()));
            wxOrderEntity.setPayDate(DateUtils.string2Time(transaction.getSuccessTime()));
            wxOrderEntity.setPayStatus(1);
            wxOrderEntity.setPayNonce(nonce);
            wxOrderEntity.setTransactionId(transaction.getTransactionId());
            //修改订单信息
            wxOrderDataService.updateById(wxOrderEntity);
            //同时处理支付记录
            QueryWrapper<WxPayLogEntity> payWrapper = new QueryWrapper<>();
            wrapper.eq("out_trade_no", transaction.getOutTradeNo());
            wrapper.eq("pay_status", 1);//支付
            WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
            if (wxPayLogEntity == null) {
                wxPayLogEntity = new WxPayLogEntity();
                wxPayLogEntity.setCreateTime(new Date());
                wxPayLogEntity.setOutTradeNo(wxOrderEntity.getOutTradeNo());
                wxPayLogEntity.setPayStatus(1);
                wxPayLogEntity.setTotalFee(wxOrderEntity.getTotalFee());
                wxPayLogEntity.setTransactionId(wxOrderEntity.getTransactionId());
                wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
                wxPayLogDataService.insert(wxPayLogEntity);
            }
        }

        returnMap.put("code", "SUCCESS");
        returnMap.put("message", "成功");
        return JSONObject.toJSONString(returnMap);
    }

    /***
     * 根据商户订单号 或者微信订单号 查询订单 outTradeNo
     * @param req
     * @return
     */
    @Transactional
    public ResponseResult queryOrderByOrderNo(QueryOrderReq req) throws ParseException {
        QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
        queryRequest.setMchid(wxPayConfig.getMerchantId());
        queryRequest.setOutTradeNo(req.getOrderNo());
        try {
            JsapiServiceExtension service =
                    new JsapiServiceExtension.Builder()
                            .config(rsaAutoCertificateConfig)
                            .signType("RSA") // 不填默认为RSA
                            .build();
           com.wechat.pay.java.service.payments.model.Transaction result = service.queryOrderByOutTradeNo(queryRequest);
            LinkedHashMap retmap = new LinkedHashMap();
            //支付成功
            if (Transaction.TradeStateEnum.SUCCESS.name().equals(result.getTradeState().name())) {
                log.info("内部订单号【{}】,微信支付订单号【{}】支付成功", result.getOutTradeNo(), result.getTransactionId());
                retmap.put("out_trade_no", result.getOutTradeNo());
                retmap.put("transaction_id", result.getTransactionId());
                retmap.put("success", true);
                retmap.put("msg", "支付成功！");
                retmap.put("success_time", DateUtils.string2Time(result.getSuccessTime()));
                //主动查询
                QueryWrapper<WxOrderEntity> wrapper = new QueryWrapper<>();
                wrapper.eq("out_trade_no", req.getOrderNo());
                WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
                if (wxOrderEntity != null) {
                    if (wxOrderEntity.getPayStatus() != 1) {
                        wxOrderEntity.setPayStatus(1);
                        wxOrderEntity.setTransactionId(result.getTransactionId());
                        wxOrderEntity.setPayDate(DateUtils.string2Date(result.getSuccessTime()));
                        wxOrderEntity.setPayTime(DateUtils.string2Time(result.getSuccessTime()));
                        wxOrderEntity.setUpdateTime(new Date());
                        wxOrderDataService.updateById(wxOrderEntity);
                        //同时处理支付记录
                        QueryWrapper<WxPayLogEntity> payWrapper = new QueryWrapper<>();
                        wrapper.eq("out_trade_no", req.getOrderNo());
                        WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
                        if (wxPayLogEntity == null) {
                            wxPayLogEntity = new WxPayLogEntity();
                            wxPayLogEntity.setCreateTime(new Date());
                            wxPayLogEntity.setOutTradeNo(wxOrderEntity.getOutTradeNo());
                            wxPayLogEntity.setPayStatus(1);
                            wxPayLogEntity.setTotalFee(wxOrderEntity.getTotalFee());
                            wxPayLogEntity.setTransactionId(result.getTransactionId());
                            wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
                            wxPayLogDataService.insert(wxPayLogEntity);
                        }
                    }
                }
            } else {
                log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
                retmap.put("out_trade_no", result.getOutTradeNo());
                retmap.put("transaction_id", result.getTransactionId());
                retmap.put("success", false);
                retmap.put("msg", "支付失败！");
                retmap.put("success_time", null);
            }
            return RestResultUtil.success(retmap);
        } catch (ServiceException e) {
            log.error("订单查询失败，返回码：{},返回信息：{}", e.getErrorCode(), e.getErrorMessage());
            return RestResultUtil.error("订单查询失败！");
        }
    }
    /***
     * 根据支付订单号查询订单 paymentNo
     * @param req
     * @return
     */
    @Transactional
    public ResponseResult queryOrderByPaymentNo(QueryOrderReq req) throws ParseException {
        QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
        queryRequest.setMchid(wxPayConfig.getMerchantId());
        queryRequest.setTransactionId(req.getPaymentNo());
        try {
            JsapiServiceExtension service =
                    new JsapiServiceExtension.Builder()
                            .config(rsaAutoCertificateConfig)
                            .signType("RSA") // 不填默认为RSA
                            .build();
            com.wechat.pay.java.service.payments.model.Transaction result = service.queryOrderById(queryRequest);
            LinkedHashMap map = new LinkedHashMap();
            //支付成功
            if (Transaction.TradeStateEnum.SUCCESS.name().equals(result.getTradeState().name())) {
                log.info("内部订单号【{}】,微信支付订单号【{}】支付成功", result.getOutTradeNo(), result.getTransactionId());
                map.put("out_trade_no", result.getOutTradeNo());
                map.put("transaction_id", result.getTransactionId());
                map.put("success", true);
                map.put("msg", "支付成功！");
                map.put("success_time", DateUtils.string2Time(result.getSuccessTime()));
                //主动查询
                QueryWrapper<WxOrderEntity> wrapper = new QueryWrapper<>();
                wrapper.eq("out_trade_no", req.getOrderNo());
                WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
                if (wxOrderEntity != null) {
                    if (wxOrderEntity.getPayStatus() != 1) {
                        wxOrderEntity.setPayStatus(1);
                        wxOrderEntity.setPayDate(DateUtils.string2Date(result.getSuccessTime()));
                        wxOrderEntity.setPayTime(DateUtils.string2Time(result.getSuccessTime()));
                        wxOrderEntity.setUpdateTime(new Date());
                        wxOrderDataService.updateById(wxOrderEntity);
                        //同时处理支付记录
                        //同时处理支付记录
                        QueryWrapper<WxPayLogEntity> payWrapper = new QueryWrapper<>();
                        wrapper.eq("out_trade_no", req.getOrderNo());
                        WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
                        if (wxPayLogEntity == null) {
                            wxPayLogEntity = new WxPayLogEntity();
                            wxPayLogEntity.setCreateTime(new Date());
                            wxPayLogEntity.setOutTradeNo(wxOrderEntity.getOutTradeNo());
                            wxPayLogEntity.setPayStatus(1);
                            wxPayLogEntity.setTotalFee(wxOrderEntity.getTotalFee());
                            wxPayLogEntity.setTransactionId(result.getTransactionId());
                            wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
                            wxPayLogDataService.insert(wxPayLogEntity);
                        }
                    }
                }
            } else {
                log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
                map.put("out_trade_no", result.getOutTradeNo());
                map.put("transaction_id", result.getTransactionId());
                map.put("success", false);
                map.put("msg", "支付失败！");
                map.put("success_time", null);
            }
            return RestResultUtil.success(map);
        } catch (ServiceException e) {
            log.error("订单查询失败，返回码：{},返回信息：{}", e.getErrorCode(), e.getErrorMessage());
            return RestResultUtil.error("订单查询失败！");
        }
    }

    /***
     * 微信申请退款
     * @param outTradeNo 商户订单号
     * @param totalAmount
     * @return
     */
    public ResponseResult createRefund(String outTradeNo, Long totalAmount) {
        //返回参数
        LinkedHashMap map = new LinkedHashMap();
        map.put("out_trade_no", outTradeNo);
        map.put("success", false);
        map.put("msg", "正在申请退款中！");
        String outRefundNo = "REFUND_" + outTradeNo;
        map.put("out_refund_no", outRefundNo);
        //申请退款订单，需要变更订单记录
        QueryWrapper<WxOrderEntity> wrapper = new QueryWrapper<>();
        wrapper.eq("out_trade_no", outTradeNo);
        WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
        if (wxOrderEntity == null) {
            return RestResultUtil.error("订单不存在，申请退款不存在！");
        }
        wxOrderEntity.setPayStatus(4);//退款中
        wxOrderEntity.setUpdateTime(new Date());
        wxOrderDataService.updateById(wxOrderEntity);
        try {
            // 构建退款service
            RefundService service = new RefundService.Builder()
                    .config(rsaAutoCertificateConfig)
                    .build();
            CreateRequest request = new CreateRequest();
            // 调用request.setXxx(val)设置所需参数，具体参数可见Request定义
            request.setOutTradeNo(outTradeNo);
            request.setOutRefundNo(outRefundNo);

            AmountReq amount = new AmountReq();
            amount.setTotal(totalAmount);
            amount.setRefund(totalAmount);
            amount.setCurrency("CNY");

            request.setAmount(amount);
            request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());

            //接收退款返回参数
            Refund refund = service.create(request);
            log.info("退款返回信息：{}", refund);
            if (refund.getStatus().equals(Status.SUCCESS)) {
                map.put("success", true);
                map.put("msg", "退款成功！");
                //说明退款成功，开始接下来的业务操作
                //主动查询
                QueryWrapper<WxOrderEntity> againWrapper = new QueryWrapper<>();
                againWrapper.eq("out_trade_no", outTradeNo);
                WxOrderEntity orderEntity = wxOrderDataService.selectOne(againWrapper);
                if (orderEntity != null) {
                    orderEntity.setPayStatus(3);//退款成功
                    orderEntity.setUpdateTime(new Date());
                    wxOrderDataService.updateById(orderEntity);
                    //同时处理退款记录
                    QueryWrapper<WxPayLogEntity> payWrapper = new QueryWrapper();
                    payWrapper.eq("out_trade_no", outTradeNo);
                    payWrapper.eq("pay_status", 2);//退款
                    WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
                    if (wxPayLogEntity == null) {
                        wxPayLogEntity = new WxPayLogEntity();
                        wxPayLogEntity.setCreateTime(new Date());
                        wxPayLogEntity.setOutTradeNo(outTradeNo);
                        wxPayLogEntity.setPayStatus(2);
                        wxPayLogEntity.setTotalFee(totalAmount.intValue());
                        wxPayLogEntity.setTransactionId(wxOrderEntity.getTransactionId());
                        wxPayLogEntity.setOutRefundNo(outRefundNo);
                        wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
                        wxPayLogDataService.insert(wxPayLogEntity);
                    }
                }
            }
            else if (refund.getStatus().equals(Status.ABNORMAL)) {
                map.put("success", false);
                map.put("msg", "退款失败，账户异常！");
                map.put("transaction_id", refund.getTransactionId());
            } else if (refund.getStatus().equals(Status.PROCESSING)) {
                map.put("success", true);
                map.put("msg", "正在退款中！");
                map.put("transaction_id", refund.getTransactionId());
            } else if (refund.getStatus().equals(Status.CLOSED)) {
                map.put("success", false);
                map.put("msg", "用户余额不足或者订单超过退款期限！");
                map.put("transaction_id", refund.getTransactionId());
            }

        } catch (ServiceException e) {
            log.error("退款失败！，错误信息：{}", e.getMessage());
            return RestResultUtil.error("退款失败！");
        } catch (Exception e) {
            log.error("服务返回成功，返回体类型不合法，或者解析返回体失败，错误信息：{}", e.getMessage());
            return RestResultUtil.error("退款失败！");
        }
        return RestResultUtil.success(map);
    }

    /***
     * 微信退款回调
     * @param request
     * @throws Exception
     */
    public void refundNotify(HttpServletRequest request) throws Exception {
        try {
            log.info("------收到退款通知------");
            // 请求头Wechatpay-Signature
            String signature = request.getHeader("Wechatpay-Signature");
            // 请求头Wechatpay-nonce
            String nonce = request.getHeader("Wechatpay-Nonce");
            // 请求头Wechatpay-Timestamp
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            // 微信支付证书序列号
            String serial = request.getHeader("Wechatpay-Serial");
            // 签名方式
            String signType = request.getHeader("Wechatpay-Signature-Type");
            // 构造 RequestParam
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(serial)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    .signType(signType)
                    .body(HttpServletUtils.getRequestBody(request))
                    .build();

            // 初始化 NotificationParser
            NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            log.info("验签参数：{}", requestParam);
            RefundNotification parse = parser.parse(requestParam, RefundNotification.class);
            log.info("验签成功！-退款回调结果：{}", parse.toString());
            //parse.getRefundStatus().equals("SUCCESS");说明退款成功
            String refundStatus = parse.getRefundStatus().toString();
            log.info("getRefundStatus状态：{}", refundStatus);

            if (refundStatus.equals("SUCCESS")) {
                log.info("成功进入退款回调，状态：{}", parse.getRefundStatus());
                //你的业务代码
                QueryWrapper<WxOrderEntity> wrapper = new QueryWrapper<>();
                wrapper.eq("out_trade_no", parse.getOutTradeNo());
                WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
                log.info("订单数据：{}", wxOrderEntity);
                if (wxOrderEntity != null) {
                    wxOrderEntity.setUpdateTime(new Date());
                    wxOrderEntity.setPayStatus(3);//退款成功
                    wxOrderEntity.setPayNonce(nonce);
                    wxOrderDataService.updateById(wxOrderEntity);
                    //同时处理退款记录
                    QueryWrapper<WxPayLogEntity> payWrapper = new QueryWrapper<>();
                    payWrapper.eq("out_trade_no", parse.getOutTradeNo());
                    payWrapper.eq("pay_status", 2);//退款
                    payWrapper.eq("out_refund_no", parse.getOutRefundNo());//退款
                    WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
                    if (wxPayLogEntity == null) {
                        wxPayLogEntity = new WxPayLogEntity();
                        wxPayLogEntity.setCreateTime(new Date());
                        wxPayLogEntity.setOutTradeNo(parse.getOutTradeNo());
                        wxPayLogEntity.setPayStatus(2);
                        wxPayLogEntity.setTotalFee(parse.getAmount().getRefund().intValue());
                        wxPayLogEntity.setTransactionId(parse.getTransactionId());
                        wxPayLogEntity.setOutRefundNo(parse.getOutRefundNo());
                        wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
                        wxPayLogDataService.insert(wxPayLogEntity);
                    }
                }
            }
        } catch (Exception e) {
            log.info("退款回调失败！错误信息：{}", e.getMessage());
        }
    }


}
